# -*- coding: utf-8 -*-

from django.db import models
from django.db.models.signals import pre_delete, post_save
from django.dispatch import receiver
import json
from metlib.misc.misc import str2obj
from weblib.django_kits import *
from weblib.django.user import *
from weblib.django.setting import *
from .dataset_mold import *

# Create your models here.
class DatasetTag(models.Model):
    """DatasetTag用来记录数据集的分类标签
    """
    class Meta:
        pass

    name = models.CharField(max_length=32, unique=True, primary_key=True)

    def __unicode__(self):
        return self.name


def get_dataset_tag(tag_name):
    try:
        tag = DatasetTag.objects.get(pk=tag_name)
    except DatasetTag.DoesNotExist:
        tag = DatasetTag(name=tag_name)
        tag.save()
    return tag

class DatasetMold(models.Model):
    """DatasetMold是数据库中用来记录Dataset模板的Model.
    """
    class Meta:
        pass

    name = models.CharField(max_length=255, unique=True, primary_key=True)  # 名字(unique)
    desc = models.TextField(default='', blank=True)  # 描述, 供其底层类型自动提供
    note = models.TextField(default='', blank=True)  # 附注, 供用户编写
    schema = models.TextField(default='{}', blank=True)  # 数据集内具体数据的uri框架
    classname = models.CharField(max_length=255, default='', blank=True)  # 实际的dataset类型的名字
    tags = models.TextField(default='', blank=True)    # 逗号分隔的标签, 可由其底层类型自动提供

    update_dt = models.DateTimeField(default=local_now)  # 更新时间

    def __unicode__(self):
        return self.name

    # 数据集模板类型
    @property
    def mold_class(self):
        return str2obj(self.classname)

    # 供生成实际数据集时所用的info字符串(json)的模板
    @property
    def info_template(self):
        return self.mold_class.info_template

    def update(self):
        """ 从模板实际的类型中更新相关信息
        :return:
        """
        try:
            schema = self.mold_class.get_schema()
            schema_json = json.dumps(schema)
            self.schema = schema_json
            self.tags = ','.join(self.mold_class.tags)
        except NotImplementedError:
            pass
        except Exception:
            pass

        try:
            desc = self.mold_class.get_mold_desc()
            self.desc = desc
        except NotImplementedError:
            pass

        self.update_dt = local_now()
        self.save()

# 需要区分一下: 此处的DataSet是数据库的记录, 而实际用来做各种逻辑处理的Dataset对象是其模板类型(mold)的具体实例.
# 此处传递后者的各样标准功能.
class DataSet(models.Model):
    class Meta:
        pass

    # name: 名字(unique), 描述
    name = models.CharField(max_length=64, primary_key=True, unique=True)  #必须是纯英文

    desc = models.TextField(default='', blank=True)
    creator = models.CharField(max_length=64, default='', blank=True)   # 创建者
    version = models.CharField(max_length=255, default='', blank=True)   # 版本信息

    tags = models.ManyToManyField(DatasetTag, blank=True) # 标签
    priority = models.IntegerField(default=9999)  # 优先级, 越小越优先
    public = models.BooleanField(default=True)   # 如果为False, 则只有特定用户可查看到此数据集
    free = models.BooleanField(default=False)  # False: 使用数据集内部的策略. True: 覆盖数据集内部的策略, 使之完全为free.

    protect_data = models.BooleanField(default=True)

    mold = models.ForeignKey(DatasetMold)  # mold: dataset的模板
    info_json = models.TextField(default='{}')  # 详细信息的json表达

    depot = models.ForeignKey('depot.Depot', blank=True, null=True)
    custom_root_path = models.CharField(max_length=255, default=None, null=True, blank=True)
    size = models.BigIntegerField(default=0)  # 数据集所占的磁盘空间大小

    update_dt = models.DateTimeField(default=local_now)  # 数据集信息的更新时间

    # 获取其实际的dataset对象
    @property
    def dataset(self):
        if self.name in _dataset_cache:
            return _dataset_cache[self.name]
        else:
            ds = self.mold.mold_class(name=self.name, uri=self.name, info=self.info, root_path=self.root_path, db_obj=self)  # TODO: 还需什么参数?
            _dataset_cache[self.name] = ds
            return ds

    # 获取其详细信息对象
    @property
    def info(self):
        return json.loads(self.info_json, strict=False)
    @info.setter
    def info(self, value):
        if isinstance(value, (str, unicode)):
            self.info_json = value
        else:
            self.info_json = json.dumps(value, indent=4)

    # 模板名
    @property
    def mold_name(self):
        return self.mold.name

    # 数据集的存储根目录
    @property
    def root_path(self):
        if self.depot is None:
            return self.custom_root_path
        else:
            return os.path.join(self.depot.path, self.name)

    @property
    def schema(self):
        return self.dataset.schema

    def __unicode__(self):
        return u'[%s] %s' % (self.mold_name, self.name)

    # TODO: 过时, 使用utils.user_owns_data(user, datauri)
    def is_free(self, uri=None, user=None, *args, **kwargs):
        if self.free:
            return True
        if user and hasattr(user, 'data_setting'):
            if user.data_setting.ignore_sample:
                return True
            if self in user.data_setting.free_datasets.all():
                return True

        return self.dataset.is_free(uri, *args, **kwargs)

    # 通过一系列参数来查找该数据集在某点或某区域的数据
    def lookup(self, info, request=None, *args, **kwargs):
        return self.dataset.lookup(info, request=request, *args, **kwargs)

    # 以uri形式来获取数据.
    def get_data(self, uri, request=None, *args, **kwargs):
        return self.dataset.get_data(uri, request=request, *args, **kwargs)

    # 以uri形式来获取文件.
    def get_file(self, uri, dest, request=None, *args, **kwargs):
        return self.dataset.get_file(uri, dest, request=request, *args, **kwargs)

    # 以uri形式来获取文件.
    def get_schema(self, request=None, *args, **kwargs):
        return self.dataset.get_schema(request=request, *args, **kwargs)


# dataset删除时, 需更新其所在的depot的大小
@receiver(pre_delete, sender=DataSet, dispatch_uid='DATASET_DEL_DUID')
def on_DataSet_delete(**kwargs):
    instance = kwargs['instance']
    _dataset_cache.pop(instance.name, '')
    size = instance.size
    if instance.depot is not None:
        depot = instance.depot
        depot.update_used_size(-size)
    if instance.root_path is not None and instance.root_path != '':
        if not instance.protect_data:
            RM(instance.root_path)

# dataset保存时, 需更新_dataset_cache
@receiver(post_save, sender=DataSet, dispatch_uid='DATASET_SAVE_DUID')
def on_DataSet_save(**kwargs):
    instance = kwargs['instance']
    created = kwargs['created']

    try:
        ds = instance.mold.mold_class(name=instance.name, uri=instance.name, info=instance.info, root_path=instance.root_path)
        _dataset_cache[instance.name] = ds
        if created:
            for tag_name in ds.tags:
                tag = get_dataset_tag(tag_name)
                instance.tags.add(tag)
    except Exception as e:
        print e


class User_Data(models.Model):
    class Meta:
        pass

    key = models.CharField(max_length=300, unique=True, primary_key=True)
    create_dt = models.DateTimeField(default=local_now)
    # info = models.TextField(default='', blank=True)
    # TODO: more details about this relation.


class Company_Data(models.Model):
    class Meta:
        pass

    key = models.CharField(max_length=300, unique=True, primary_key=True)
    create_dt = models.DateTimeField(default=local_now)


class UserDataSetting(models.Model):
    class Meta:
        pass

    user = models.OneToOneField(AUTH_USER_MODEL, related_name='data_setting', on_delete=models.CASCADE)
    data_limit_per_day = models.IntegerField(default=100)
    ignore_sample = models.BooleanField(default=False)

    accessible_datasets = models.ManyToManyField('datakeeper.Dataset', related_name='accessible_user_settings', blank=True)
    free_datasets = models.ManyToManyField('datakeeper.Dataset', related_name='free_user_settings', blank=True)


class CompanyDataSetting(models.Model):
    class Meta:
        pass

    #company = models.OneToOneField('dxuser.Company', related_name='data_setting', on_delete=models.CASCADE)

    company = models.CharField(max_length=300)
    accessible_datasets = models.ManyToManyField('datakeeper.Dataset', related_name='accessible_company_settings', blank=True)
    free_datasets = models.ManyToManyField('datakeeper.Dataset', related_name='free_company_settings', blank=True)
